import os
import zipfile
import shutil
import argparse
import datetime
import stat
import time
import traceback

# 记录压缩日志的函数
def log_compressed_folder(folder_path):
    log_file = 'log.txt'
    try:
        with open(log_file, 'a', encoding='utf-8') as f:
            f.write(f"{folder_path}\n")
    except Exception as e:
        print(f"记录日志失败: {str(e)}")

def count_files(directory, cur_depth, max_depth=5):
    if cur_depth == max_depth:
        total_dirs = 0
        total_files = 0
        for root, dirs, files in os.walk(directory):
            total_dirs += len(dirs)
            total_files += len(files)
        return total_files
    """计算目录中文件和文件夹的总数"""
    for root, dirs, files in os.walk(directory):
        total_count = len(dirs) + len(files)
        return total_count

def log_error(error_type, path, error_msg):
    """记录错误日志到log_error.txt"""
    error_log_file = 'log_error.txt'
    try:
        with open(error_log_file, 'a', encoding='utf-8') as f:
            timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            f.write(f"[{timestamp}] {error_type}: {path} - {error_msg}\n")
    except Exception as e:
        print(f"记录错误日志失败: {str(e)}")

def compress_folder(folder_path):   # 增加current_depth调试bug用，解决到最大递归深度仍继续递归问题
    """将文件夹压缩为同名zip文件"""
    try:
        # if current_depth == 4:  # 调试bug用
        #     raise
        zip_file_name = os.path.basename(folder_path) + '.zip'
        # 在当前目录下存放压缩文件
        zip_file_path = os.path.join(os.path.dirname(folder_path), zip_file_name)
        
        with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, files in os.walk(folder_path):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, os.path.dirname(folder_path))
                    zipf.write(file_path, arcname)
        # 关键：显式关闭ZIP文件后等待0.5秒
        time.sleep(0.5)
        # 压缩成功后记录日志
        log_compressed_folder(folder_path)
        return zip_file_path
    except PermissionError as e:
        print(f"❌ 压缩失败: {folder_path} - 权限不足或文件被占用")
        log_error("压缩失败", folder_path, str(e))
        return None
    except Exception as e:
        print(f"❌ 压缩失败: {folder_path} - {str(e)}")
        log_error("压缩失败", folder_path, f"{str(e)}\n{traceback.format_exc()}")
        return None

def remove_directory(directory):
    """跨平台删除目录（优先使用Python，失败则调用系统命令）"""
    try:
        # 先尝试Python原生方法
        shutil.rmtree(directory, onerror=handle_remove_readonly)
        print(f"✅ Python删除成功: {directory}")
        return True
    except Exception as e:
        print(f"❌ Python删除失败: {e}，尝试系统命令...")
        try:
            if os.name == 'nt':  # Windows系统
                # 使用rmdir命令强制删除（包括只读文件）
                directory = directory.replace('/', '\\')  # 确保路径使用反斜杠
                os.system(f'rmdir /S /Q "{directory}"')
                # 或使用更强大的cmd /c命令
                # os.system(f'cmd /c "attrib -R -S -H "{directory}"\\* /S /D && rmdir /S /Q "{directory}""')
            else:  # Linux/macOS
                os.system(f'rm -rf "{directory}"')
            
            # 验证是否删除成功
            if not os.path.exists(directory):
                print(f"✅ 系统命令删除成功: {directory}")
                return True
            else:
                print(f"❌ 系统命令删除失败，目录仍存在: {directory}")
                return False
        except Exception as cmd_e:
            print(f"❌ 系统命令执行失败: {cmd_e}")
            return False


def handle_remove_readonly(func, path, exc_info):
    """处理只读文件的删除"""
    import stat
    if not os.access(path, os.W_OK):
        os.chmod(path, stat.S_IWUSR)
        func(path)
    else:
        print(f"❌ 无法删除 {path}: {exc_info[1]}")
        traceback.print_exc()

def process_folders(directory, threshold, max_depth=5, current_depth=0, total_entry=0):
    """处理文件夹，递归压缩超过阈值的子文件夹"""
    if not os.path.isdir(directory):
        return
    if current_depth == max_depth:
        print(f"❗ 达到最大递归深度({max_depth})，处理最后一层递归，阻止继续递归: {directory}")
        total_count = count_files(directory, current_depth, max_depth)
        if total_count >= threshold:
            print(f"❗ 最后一层目录({directory})文件数量({total_count})超过阈值({threshold})，开始压缩...")
            try:
                zip_file_path = compress_folder(directory)   # 增加参数current_depth调试用
                if zip_file_path:
                    print(f"✅ 压缩完成: {zip_file_path}")
                    # if os.name == 'nt':  # Windows系统
                    #     directory = fr"\\?\{os.path.abspath(directory)}"
                    #     # 尝试删除前刷新文件系统缓存（Windows特有）
                    #     os.system('fsutil file setvaliddata 0')
                    # shutil.rmtree(directory)
                    flag = remove_directory(directory)
                    return
                else:
                    return   # 关键修改，解决到max_depth后，如果压缩报错，继续向下递归的问题
            except Exception as e:
                print(f"⚠️ 其他错误，跳过目录: {directory} (错误: {str(e)})")
                traceback.print_exc()  # 打印完整堆栈信息
                log_error("删除失败", directory, f"{str(e)}\n{traceback.format_exc()}")
                return
        else:
            return
    
    # 打印当前层级和目录信息
    indent = "  " * current_depth
    print(f"{indent}└── 正在处理第{current_depth}层目录: {directory}")
    # 打印这一层文件数量
    cur_total_entry = len(os.listdir(directory))
    print(f"{indent}└── 第{current_depth}层目录处理完成，文件总数: {cur_total_entry}")
    # 这里加 1 不影响递归调用传入的参数
    current_depth += 1 
    # 如果当前层内容数量超过阈值，则压缩该目录, 不继续向下递归
    if cur_total_entry >= threshold:
        try:
            zip_file_path = compress_folder(directory)
            if zip_file_path:
                print(f"✅ 压缩完成: {zip_file_path}")
                # shutil.rmtree(directory, onerror=handle_remove_readonly)  # 关键改动
                flag = remove_directory(directory)
                return
        except Exception as e:
            print(f"⚠️ 其他错误，跳过目录: {directory} (错误: {str(e)})")
            traceback.print_exc()  # 打印完整堆栈信息
            log_error("删除失败", directory, f"{str(e)}\n{traceback.format_exc()}")
            return

    # 向下递归判断，这里用的是遍历的思想
    for entry in os.scandir(directory):
        if entry.is_dir():
            subdir_path = entry.path
            try:
                # 正确做法：在递归调用时让 current_depth 加 1
                process_folders(subdir_path, threshold, max_depth, current_depth, total_entry)
            except PermissionError as e:
                print(f"⚠️ 权限不足，跳过目录: {subdir_path} (错误: {str(e)})")
                log_error("权限不足", subdir_path, str(e))
                continue
        else:
            pass
    return

            
def main():
    parser = argparse.ArgumentParser(description='文件夹压缩工具')
    parser.add_argument('-d', '--directory', default=r"D:\songlin\1_临时传输\文件转移", 
                       help='要处理的目录路径，默认为当前目录')
    parser.add_argument('-t', '--threshold', type=int, default=100,
                       help='文件数量阈值，超过则压缩，默认1000')
    parser.add_argument('-m', '--max-depth', type=int, default=4,
                       help='递归处理的最大深度，默认为5')
    args = parser.parse_args()
    
    process_folders(args.directory, threshold=args.threshold, max_depth=args.max_depth)

if __name__ == '__main__':
    main()